#include "StdAfx.h"
#include "Terrain.h"
#include "Vertex.h"
#include <vector>
#include <algorithm>


const float Terrain::HEIGHT_AMP = 0.3f;
const float Terrain::HEIGHT_ANG = 0.1f;

float Terrain::getHeight(float x, float z) const
{
	return HEIGHT_AMP*( z * sinf(HEIGHT_ANG*x) + x * cosf(HEIGHT_ANG*z) );
}


void Terrain::init(ID3D10Device *device, DWORD m, DWORD n, float dx)
{
	this->md3dDevice = device;
	buildBuffers(device, m, n, dx);
	buildResourceViews();
}

void Terrain::update(float dt)
{
}

void Terrain::draw()
{
	// opaque
	float blendFactor[] = {0.0f, 0.0f, 0.0f, 0.0f};
	md3dDevice->OMSetBlendState(0, blendFactor, 0xffffffff);

	UINT stride = sizeof(Vertex);
	UINT offset = 0;

	md3dDevice->IASetVertexBuffers(0, 1, &mVertexBuffer, &stride, &offset);
	md3dDevice->IASetIndexBuffer(mIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
	md3dDevice->DrawIndexed(this->mNumIndices, 0, 0);

	// scale tex coordinates by 5 
	D3DXMatrixScaling(&mTexMatrix, 5.0f, 5.0f, 1.0f);
}

void Terrain::buildBuffers(ID3D10Device *device, DWORD m, DWORD n, float dx)
{
	this->mNumRows = m;
	this->mNumCols = n;

	this->mNumVertices = m * n;
	this->mNumIndices = 3 * (m-1) * (n-1) * 2;

	// create geometry
	std::vector<Vertex> vertices(this->mNumVertices);
	float halfWidth = (n-1) * dx * 0.5f;
	float halfDepth = (m-1) * dx * 0.5f;

	float du = 1.0f / (n-1);
	float dv = 1.0f / (m-1);

	for (DWORD i = 0; i < m; i++) {
		float z = halfDepth - i*dx;

		for (DWORD j = 0; j < n; j++) {
			float x = -halfWidth + j*dx;
			float y = getHeight(x, z);

			// pos
			Vertex &v = vertices[i*n+j];
			v.pos = D3DXVECTOR3(x, y, z);

			// tex
			v.texC.x = j * du;
			v.texC.y = i * dv;

			// normal
			D3DXVECTOR3 normal;
			normal.x = -HEIGHT_AMP * ( 0.1f*z*cosf(HEIGHT_ANG*x) +        cosf(HEIGHT_ANG*z) );
			normal.z = -HEIGHT_AMP * (        sinf(HEIGHT_ANG*x) - 0.1f*x*sinf(HEIGHT_ANG*z) );
			normal.y = 1.0f;
			D3DXVec3Normalize(&v.normal, &normal);
		}
	}

	// fill the vertex buffer
	D3D10_BUFFER_DESC vbd;
	ZeroMemory(&vbd, sizeof(vbd));
	vbd.Usage = D3D10_USAGE_IMMUTABLE;
	vbd.ByteWidth = sizeof(Vertex) * this->mNumVertices;
	vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
	D3D10_SUBRESOURCE_DATA vInitData;
	vInitData.pSysMem = &vertices[0];
	HR( device->CreateBuffer(&vbd, &vInitData, &mVertexBuffer) );

	// create the index buffer
	std::vector<DWORD> indices(this->mNumIndices);
	int k = 0;
	for (DWORD d = 0; d < m-1; ++d) {
		for (DWORD w = 0; w < n-1; ++w) {
			// a b
			// c d 
			// a->b->c, c->b->d
			indices[k]		=  w	+ n *  d;
			indices[k+1]	= (w+1)	+ n *  d;
			indices[k+2]	=  w	+ n * (d+1);
			indices[k+3]	=  w	+ n * (d+1);
			indices[k+4]	= (w+1)	+ n *  d;
			indices[k+5]	= (w+1)	+ n * (d+1);

			k += 6;
		}
	}

	D3D10_BUFFER_DESC ibd;
	ZeroMemory(&ibd, sizeof(ibd));
	ibd.Usage = D3D10_USAGE_IMMUTABLE;
	ibd.ByteWidth = sizeof(DWORD) * this->mNumIndices;
	ibd.BindFlags = D3D10_BIND_INDEX_BUFFER;
	D3D10_SUBRESOURCE_DATA iInitData;
	iInitData.pSysMem = &indices[0];
	HR( device->CreateBuffer(&ibd, &iInitData, &mIndexBuffer) );
}

void Terrain::buildResourceViews()
{
	HR( D3DX10CreateShaderResourceViewFromFile(md3dDevice,
		L"grass.dds", 0, 0, &mDiffuseMapRV, 0) );
	HR( D3DX10CreateShaderResourceViewFromFile(md3dDevice,
		L"graySpecular.dds", 0, 0, &mSpecularMapRV, 0) );
}

D3DXMATRIX Terrain::getWorldMatrix()
{
	return mWorldMatrix;
}

D3DXMATRIX	Terrain::getTexMatrix()
{
	return mTexMatrix;
}

ID3D10ShaderResourceView* Terrain::getDiffuseMap(ID3D10ShaderResourceView* defaultMapRV)
{
	if (mDiffuseMapRV == 0)
		return mDiffuseMapRV;
	return  mDiffuseMapRV;
}

ID3D10ShaderResourceView* Terrain::getSpecularMap(ID3D10ShaderResourceView* defaultMapRV)
{
	if (mSpecularMapRV == 0)
		return defaultMapRV;
	return mSpecularMapRV;
}
